/*	
 * Authors: Ken YANG <spng.yang@gmail.com>
 * 	
 * Mammoth client main program
 *
 * Copyright (C) 2007-2008 Ken YANG
 * This program is free software; you can redistribute it and/or 
 * modify it under the terms of the GNU General Public License 
 * as published by the Free Software Foundation, version 2. 
 * THIS SOFTWARE COMES "AS IS" WITHOUT ANY WARRANTIES.
 */

#include "effective_io.h"
#include "threads.h"

/* 
 * Just for readline, internal use 
 * FIXME: This is not thread safe
 * */
static int read_cnt=0;
static char *read_ptr;
static char read_buf[MAXLINE];
/* End - readline */

/* 
 * For readline_r, using thread-specific data
 * */

static pthread_key_t rl_key;
static pthread_once_t rl_once=PTHREAD_ONCE_INIT;

static void 
readline_destructor(void *ptr)
{
	free(ptr);
}

static void 
readline_once(void)
{
	Pthread_key_create(&rl_key,readline_destructor);
}

struct rline{
	int rl_cnt;
	char *rl_buffptr;	/* initialize to rl_buf */
	char rl_buf[MAXLINE];
};

/* End - readline_r */


/* 
 * Internal func called by readline 
 * FIXME: This is not thread-safe
 * */
static ssize_t pre_read(int fd, char *ptr)
{
	if(read_cnt<=0){
again:
		if((read_cnt=read(fd, read_buf, sizeof(read_buf)))<0){
			if(errno==EINTR)
				goto again;
			return -1;
		}else if(read_cnt==0)
			return 0;
		read_ptr=read_buf;
	}
	read_cnt--;
	*ptr=*(read_ptr++);
	return 1;
}

/* Continue to read, if had not got required data */
ssize_t keep_read(int fd, void *buf, size_t count)
{
	size_t nleft;
	ssize_t nread;
	void *ptr=buf;

	nleft=count;
	while(nleft){
		if((nread=read(fd, ptr, nleft))<0){
			/* Continue, after singal handle */
			if(errno==EINTR)
				nread=0;
			else
				return -1;
		}else if(0==nread)	/* EOF */
			break;

		nleft-=nread;
		ptr+=nread;
	}
	return (count-nleft);
}

/* Above wrapper */
ssize_t Keep_read(int fd, void *buf, size_t count)
{
	ssize_t rc;
	if((rc=keep_read(fd, buf, count))<0)
		err_sys("keep_read error");
	return rc;
}

/* Continue to write, if didn't write enough data */
ssize_t keep_write(int fd, const void *buf, size_t count)
{
	size_t nleft;
	ssize_t nwrite;
	const char *ptr;

	nleft=count;
	ptr=buf;
	while(nleft){
		if((nwrite=write(fd, ptr, nleft))<0){
			/* Call again */
			if(errno==EINTR)
				nwrite=0;
			else 
				return -1;
		}
		nleft-=nwrite;
		ptr+=nwrite;
	}
	return (count);
}

/* Above wrapper */
void Keep_write(int fd, const void *buf, size_t count)
{
	if(keep_write(fd, buf, count)!=count)
		err_sys("keep_write error");
}

/* 
 * Read a Line. For performance, use buffer. However 
 * for avoiding the problem incurred by standard I/O, 
 * we use own buffer to make caller check the status 
 * of the buffer.
 * */

ssize_t readline(int fd, void *ptr, size_t maxlen)
{
	int i;
	ssize_t rc;
	char c, *vptr;
	
	vptr=ptr;
	for(i=1;i<maxlen;i++){
		if((rc=pre_read(fd, &c))==1){
			*(vptr++)=c;
			/* newline is stored */
			if(c=='\n') 
				break;
		}else if(rc==0){
			/* EOF, i-1 byte was read */
			*vptr=0;
			return (i-1);
		}else	/* error, errno was set by read */
			return -1;
	}
	*vptr=0;	/* null terminated */
	return i;
}

ssize_t Readline(int fd, void *ptr, size_t maxlen)
{
	int rc;
	if((rc=readline(fd, ptr, maxlen))<0)
		err_sys("readline error");
	return rc;
}

/* 
 * Internal func called by readline_r 
 * */
static ssize_t pre_read_r(struct rline *tsd, int fd, char *ptr)
{
	if(tsd->rl_cnt<=0){
again:
		if((tsd->rl_cnt=read(fd,tsd->rl_buf,MAXLINE))<0){
			if(errno==EINTR)
				goto again;
			return -1;
		}else if(tsd->rl_cnt==0)
			return 0;
		tsd->rl_bufptr=tsd->rl_buf;
	}
	tsd->rl_cnt--;
	*ptr=*(tsd->rl_bufptr++);
	return 1;
}

/* reentrant version of readline */
ssize_t readline_r(int fd, void *ptr, size_t maxlen)
{
	int i;
	ssize_t rc;
	char c, *vptr;
	struct rline *tsd;

	Pthread_once(&rl_once,readline_once);
	if((tsd=(struct rline *)pthread_getspecific(rl_key))==NULL){
		tsd=(struct rline *)Calloc(sizeof(struct rline));
		Pthread_setspecific(rl_key, tsd);
	}
	
	vptr=ptr;
	for(i=1;i<maxlen;i++){
		if((rc=pre_read(tsd, fd, &c))==1){
			*(vptr++)=c;
			/* newline is stored */
			if(c=='\n') 
				break;
		}else if(rc==0){
			/* EOF, i-1 byte was read */
			*vptr=0;
			return (i-1);
		}else	/* error, errno was set by read */
			return -1;
	}
	*vptr=0;	/* null terminated */
	return i;
}


/* 
 * Check the status of buffer.
 *
 * return the bytes left in buffer, and the pointer 
 * to the "left" position
 *
 * */
ssize_t readlinebuf(void *vptr)
{
	if(read_cnt)
		vptr=read_ptr+read_cnt;
	return read_cnt;
}


/* Read Wrapper, including error handle */
ssize_t Read(int fd, void *buf, size_t count)
{
	ssize_t rc;

	if((rc=read(fd, buf, count))==-1)
		err_sys("read error");

	return rc;
}

void Write(int fd, void *buf, size_t count)
{
	if(write(fd, buf, count)!=count)
		err_sys("write error");
}

/* Standard I/O wrapper, including error handle */
char *Fgets(char *s, int size, FILE *stream)
{
	char *rptr;
	if((rptr=fgets(s, size, stream))==NULL && (ferror(stream)!=0))
		err_sys("fgets error");
	return rptr;
}

void Fputs(const char *s, FILE *stream)
{
	if(fputs(s, stream)==EOF)
		err_sys("fputs error");
}



